
//11-04
#ifndef VECTOR_2_H
#define VECTOR_2_H

#include <vector>
#include <type_traits>
#include <iostream>
#include <assert.h>
#include "zero_value.h"

GEO_ALG_BEGIN_NAMESPACE

#define VALUE_TYPE_CHECK static_assert(\
                                       (std::is_same<value_type, float>::value)     ||\
                                       (std::is_same<value_type, double>::value)    ||\
                                       (std::is_same<value_type, int>::value)       ||\
                                       (std::is_same<value_type, short>::value)     ||\
                                       (std::is_same<value_type, long>::value)      ||\
                                       (std::is_same<value_type, __int64>::value)     \
                                       , "not value type");


template <class T>
class Point3 {
public:
    typedef typename T          value_type;
    typedef typename Point3<T>  point_type;

public:

    point_type(value_type x = 0, value_type y = 0, value_type z = 0) {
        x_ = x;
        y_ = y;
        z_ = z;
    }
    point_type(const point_type& other) {
        x_ = other.x_;
        y_ = other.y_;
        z_ = other.z_;
    }

public:
    enum {
        dimension_nums = 3
    };
    union {
        struct {
            value_type   x_;
            value_type   y_;
            value_type   z_;
        };
        value_type   v_[dimension_nums];
    };
};

template <class T>
class Point2 {
public:
    typedef typename T          value_type;
    typedef typename Point2<T>  point_type;

public:
    point_type(value_type x = 0, value_type y = 0) {
        x_ = x;
        y_ = y;
    }

    point_type(const point_type& other) {
        x_ = other.x_;
        y_ = other.y_;
    }

public:
    enum {
        dimension_nums = 2
    };
    union {
        struct {
            value_type   x_;
            value_type   y_;
        };
        value_type       v_[dimension_nums];
    };

};

template<class P>
class  PointBase : public P {
public:
    typedef typename P::value_type              value_type;
    typedef typename P                          point_type;
    typedef typename PointBase<P>               base_type;
    typedef typename PointBase<P>&              reference;
    typedef typename const PointBase<P>&        const_ref;
    typedef typename ZeroType<value_type>       zero_type;
    VALUE_TYPE_CHECK

    base_type()
        : point_type((value_type)0) {
    }

    base_type(value_type x, value_type y)
        : point_type(x, y) {
    }

    base_type(value_type x, value_type y, value_type z)
        : point_type(x, y, z) {
    }


    base_type(const value_type* nums) {
        for (int i = 0; i < dimension_nums; i++) {
            v_[i] = nums[i];
        }
    }

    base_type(const point_type& other) {
        for (int i = 0; i < dimension_nums; i++) {
            v_[i] = other.v_[i];
        }
    }

    base_type(const_ref other) {
        for (int i = 0; i < dimension_nums; i++) {
            v_[i] = other.v_[i];
        }
    }

public:

    base_type& operator=(const_ref other) {
        for (int i = 0; i < dimension_nums; i++) {
            v_[i] = other.v_[i];
        }
        return *this;
    }

    base_type operator+(const_ref other)const {
        base_type ret = *this;
        for (int i = 0; i < dimension_nums; i++) {
            ret.v_[i] += other.v_[i];
        }
        return ret;
    }

    base_type operator-(const_ref other)const {
        base_type ret = *this;
        for (int i = 0; i < dimension_nums; i++) {
            ret.v_[i] -= other.v_[i];
        }
        return ret;
    }

    /*
    * @brief:   operator* vec dot product
    * @param:   const base_type & other
    * @return:  base_type
    */
    value_type operator*(const_ref other)const {
        value_type sum = value_type(0);
        for (int i = 0; i < dimension_nums; i++) {
            sum += (this->v_[i] * other.v_[i]);
        }
        return sum;
    }



    reference operator+=(const_ref other) {
        for (int i = 0; i < dimension_nums; i++) {
            this->v_[i] += other.v_[i];
        }
        return *this;
    }

    reference operator-=(const_ref other) {
        for (int i = 0; i < dimension_nums; i++) {
            this->v_[i] -= other.v_[i];
        }
        return *this;
    }

    base_type operator*(const value_type& value)const {
        base_type ret = *this;
        for (int i = 0; i < dimension_nums; i++) {
            ret.v_[i] *= value;
        }
        return ret;
    }

    base_type operator/(const value_type& value)const {
        assert(!zero_type::is_zero(value));
        base_type ret = *this;
        for (int i = 0; i < dimension_nums; i++) {
            ret.v_[i] /= value;
        }
        return ret;
    }


    reference operator*=(const value_type& value) {
        for (int i = 0; i < dimension_nums; i++) {
            this->v_[i] *= value;
        }
        return *this;
    }

    reference operator/=(const value_type& value) {
        assert(!zero_type::is_zero(value));
        for (int i = 0; i < dimension_nums; i++) {
            this->v_[i] /= value;
        }
        return *this;
    }

    bool operator==(const point_type& other)const {
        for (int i = 0; i < dimension_nums; i++) {
            if (!zero_type::is_zero(v_[i] - other.v_[i])) {
                return false;
            }
        }
        return true;
    }

    bool operator!=(const point_type& other)const {
        return !(*this == other);
    }

    bool operator>=(const point_type& other)const {
        for (int i = 0; i < dimension_nums; i++) {
            if (v_[i] < other.v_[i]) {
                return false;
            }
        }
        return true;
    }

    bool operator>(const point_type& other)const {
        for (int i = 0; i < dimension_nums; i++) {
            if (v_[i] <= other.v_[i]) {
                return false;
            }
        }
        return true;
    }

    bool operator<=(const point_type& other)const {
        return !(*this > other);
    }

    bool operator<(const point_type& other)const {
        return !(*this >= other);
        return true;
    }


    double mod() const {
        value_type sum = 0;
        for (int i = 0; i < dimension_nums; i++) {
            sum += v_[i] * v_[i];
        }
        return sqrt(sum);
    }

    reference normalized() {
        assert(!zero_type::is_zero(mod()));
        *this /= mod();
        return *this;
    }

    bool is_zero()const {
        return ZeroType<double>::is_zero(mod());
    }


};


GEO_ALG_END_NAMESPACE
#endif
